﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Reflection;
using MySql.Data.MySqlClient;

namespace RedmineIssuesProvider
{
    [TypeConverter(typeof(MyEnumConverter))]
    public enum EqualNotEqual
    {
        [Description("=")]
        Equal,
        [Description("<>")]
        NotEqual
    }

    [TypeConverter(typeof(MyEnumConverter))]
    public enum OpenClosed
    {
        [Description("открыт")]
        Open,
        [Description("закрыт")]
        Closed,
        [Description("=")]
        Equal,
        [Description("<>")]
        NotEqual,
    }

    [TypeConverter(typeof(MyEnumConverter))]
    public enum AssignedCondition
    {
        [Description("<мне>")]
        Me,
        [Description("=")]
        Equal,
        [Description("<>")]
        NotEqual,
        [Description("<отсутствует>")]
        None
    }

    [TypeConverter(typeof(MyEnumConverter))]
    public enum DueDateCondition
    {
        [Description("менее чем")]
        Less,
        [Description("более чем")]
        More,
        [Description("в")]
        Equal,
        [Description("сегодня")]
        Today,
        [Description("на этой неделе")]
        ThisWeek,
        [Description("менее, чем дней назад")]
        LessAgo,
        [Description("более, чем дней назад")]
        MoreAgo,
        [Description("дней назад")]
        EqualAgo
    }

    internal class MyPropInfo
    {
        public string PropName { get; set; }
        public string PropColumn { get; set; }
    }

    internal class MyPropInfoList : List<MyPropInfo>
    {
        public override string ToString()
        {
            string result = "";

            foreach (MyPropInfo info in this)
            {
                result = result + "," + info.PropColumn;
            }

            return result;
        }
    }

    public class Service
    {
        private MySqlConnection _connection;

        public List<IssueStatus> IssueStatuses { get; set; }
        public List<Tracker> Trackers { get; set; }
        public List<User> Users { get; set; }
        public List<Priority> Priorities { get; set; }
        public List<Project> Projects { get; set; }

        public Project CurrentProject { get; set; }
        public IssueList Issues { get; set; }

        public FilterList Filters { get; set; }
        public bool FiltersExist { get; set; }

        public Service()
        {
            _connection = new MySqlConnection("Server="+Properties.Settings.Default.Host+";Database="+Properties.Settings.Default.Database+
                "; User=" + Properties.Settings.Default.Login + ";Password=" + Properties.Settings.Default.Password + ";Charset=utf8;");

            try
            {
                _connection.Open();
            }
            catch (Exception)
            {
                return;
            }

            IssueStatuses = new List<IssueStatus>();
            Trackers = new List<Tracker>();
            Users = new List<User>();
            Priorities = new List<Priority>();
            Projects = new List<Project>();
            CurrentProject = null;

            Issues = new IssueList();

            Filters = new FilterList();
            TryFilters();

            MySqlCommand command = _connection.CreateCommand();
            command.CommandType = CommandType.Text;

            Fill<Project>(Projects,command,"projects");
            Fill<IssueStatus>(IssueStatuses, command, "issue_statuses");
            Fill<Tracker>(Trackers,command,"trackers");
            Fill<User>(Users,command, "users");
            Fill<Priority>(Priorities,command,"enumerations");
        }

        public void TryFilters()
        {
            try
            {
                Filters.GetAll(Properties.Settings.Default.FiltersFileName);
                FiltersExist = true;
            }
            catch (Exception)
            {
                FiltersExist = false;
            }
        }

        private void Fill<T>(List<T> obj, MySqlCommand command, string tableName) where T : Enumerations,new()
        {
            string nameColumn = "name";
            string condition = "";
            MyPropInfoList additionalProperties = new MyPropInfoList();

            if (typeof(T) == typeof(IssueStatus))
                additionalProperties.Add(new MyPropInfo { PropName = "IsClosed", PropColumn = "is_closed" });
            if (typeof(T) == typeof(User))
                nameColumn = "login";
            if (typeof(T) == typeof(Priority))
            {
                condition = " WHERE (opt = 'IPRI')";
                additionalProperties.Add(new MyPropInfo {PropName = "priority",PropColumn = "position"});
            }

            if (typeof(T) == typeof(Project))
            {
                obj.Add(new T { id = -1, name = "<все>" });
            }

            command.CommandText = "SELECT id,"+nameColumn+additionalProperties+" FROM "+tableName+condition;
            MySqlDataReader reader = command.ExecuteReader();

            while (reader.Read())
            {
                var newItem = new T { id = (int)reader["id"], name = reader[nameColumn].ToString() };

                foreach (MyPropInfo myPropInfo in additionalProperties)
                {
                    Type TypeToReflect = typeof(T);
                    PropertyInfo fieldInfo = TypeToReflect.GetProperty(myPropInfo.PropName);

                    object[] invokeAttr = { reader[myPropInfo.PropColumn] };
                    fieldInfo.GetSetMethod().Invoke(newItem, invokeAttr);

                }
                
                obj.Add(newItem);
            }
            reader.Close();
        }

        public void FillIssues()
        {
            Issues.Clear();

            MySqlCommand command = _connection.CreateCommand();
            command.CommandType = CommandType.Text;
            command.CommandText = "SELECT id,subject,tracker_id,project_id,due_date,status_id,assigned_to_id,priority_id,author_id FROM issues";

            MySqlDataReader reader = command.ExecuteReader();

            while (reader.Read())
            {
                Project project = Projects.Find(x => (x.id == (int)reader["project_id"]));
                if (CurrentProject == project || CurrentProject.id == -1)
                {
                    Issue newIssue = new Issue();
                    newIssue.id = (int)reader["id"];
                    newIssue.subject = reader["subject"].ToString();
                    newIssue.status = IssueStatuses.Find(x => (x.id == (int)reader["status_id"]));
                    newIssue.tracker = Trackers.Find(x => (x.id == (int)reader["tracker_id"]));
                    newIssue.project = project;
                    newIssue.author = Users.Find(x => (x.id == (int)reader["author_id"]));
                    newIssue.assigned_to = Users.Find(x => ((reader["assigned_to_id"].ToString() != "") && (x.id == (int)reader["assigned_to_id"])));
                    newIssue.priority = Priorities.Find(x => (x.id == (int)reader["priority_id"]));
                    newIssue.due_date = reader["due_date"].ToString() != "" ? (DateTime) reader["due_date"] : DateTime.MinValue;

                    Issues.Add(newIssue);
                }
            }
            reader.Close();

            Issues.Reverse();
        }

        #region Работа с фильтрами

        public List<Issue> FilterIssues(Filter filter, DateTime today)
        {
            return Issues.FindAll(x => (filter.Match(x,today)));
        }
        #endregion
    }
}
